﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;
using System.Globalization;
using System.Threading;

namespace MvcControlToolkit
{
    /**
     * Mask Characters and Delimiters
        9 - Only a numeric character
        L - Only a letter
        $ - Only a letter or a space
        C - Only a custom character (case sensitive)
        A - Only a letter or a custom character
        N - Only a numeric or custom character
        ? - Any character

        / - Date separator
        : - Time separator
        . - Decimal separator
        , - Thousand separator
        \ - Escape character
        { - Initial delimiter for repetition of masks
        } - Final delimiter for repetition of masks
    **/
    public static class MaskedEditExtensions
    {
        #region [General]

        public static string MaskedEdit(this AjaxHelper helper, string elementId, MaskedEditType maskType, string mask)
        {
            return helper.MaskedEdit(elementId, maskType, mask, null, null, null, null);
        }

        public static string MaskedEdit(this AjaxHelper helper, string elementId, MaskedEditType maskType, string mask, string onFocusCssClass)
        {
            return helper.MaskedEdit(elementId, maskType, mask, onFocusCssClass, null, null, null);
        }

        public static string MaskedEdit(this AjaxHelper helper, string elementId, MaskedEditType maskType, string mask, string onFocusCssClass, string onFocusCssNegative)
        {
            return helper.MaskedEdit(elementId, maskType, mask, onFocusCssClass, onFocusCssNegative, null, null);
        }

        public static string MaskedEdit(this AjaxHelper helper, string elementId, MaskedEditType maskType, string mask, string onFocusCssClass, string onFocusCssNegative, string onBlurCssNegative)
        {
            return helper.MaskedEdit(elementId, maskType, mask, onFocusCssClass, onFocusCssNegative, onBlurCssNegative, null);
        }

        public static string MaskedEdit(this AjaxHelper helper, string elementId, MaskedEditType maskType, string mask, string onFocusCssClass, string onFocusCssNegative, string onBlurCssNegative, string onInvalidCssClass)
        {
            RouteValueDictionary properties = new RouteValueDictionary();
            properties.Add("MaskType", maskType);
            properties.Add("Mask", mask);
            properties.AddIfNotDefault("OnFocusCssClass", onFocusCssClass);
            properties.AddIfNotDefault("OnFocusCssNegative", onFocusCssNegative);
            properties.AddIfNotDefault("OnBlurCssNegative", onBlurCssNegative);
            properties.AddIfNotDefault("OnInvalidCssClass", onInvalidCssClass);

            //return masked edit create statement
            return helper.MaskedEdit(elementId, properties);
        }

        #endregion

        #region [Numbers and Currency]

        public static string MaskedEdit(this AjaxHelper helper, string elementId, string mask, MaskedEditShowSymbol acceptNegative, MaskedEditShowSymbol displayMoney)
        {
            return helper.MaskedEdit(elementId, mask, acceptNegative, displayMoney, null, null, null, null);
        }

        public static string MaskedEdit(this AjaxHelper helper, string elementId, string mask, MaskedEditShowSymbol acceptNegative, MaskedEditShowSymbol displayMoney, string onFocusCssClass)
        {
            return helper.MaskedEdit(elementId, mask, acceptNegative, displayMoney, onFocusCssClass, null, null, null);
        }

        public static string MaskedEdit(this AjaxHelper helper, string elementId, string mask, MaskedEditShowSymbol acceptNegative, MaskedEditShowSymbol displayMoney, string onFocusCssClass, string onFocusCssNegative)
        {
            return helper.MaskedEdit(elementId, mask, acceptNegative, displayMoney, onFocusCssClass, onFocusCssNegative, null, null);
        }

        public static string MaskedEdit(this AjaxHelper helper, string elementId, string mask, MaskedEditShowSymbol acceptNegative, MaskedEditShowSymbol displayMoney, string onFocusCssClass, string onFocusCssNegative, string onBlurCssNegative)
        {
            return helper.MaskedEdit(elementId, mask, acceptNegative, displayMoney, onFocusCssClass, onFocusCssNegative, onBlurCssNegative, null);
        }

        public static string MaskedEdit(this AjaxHelper helper, string elementId, string mask, MaskedEditShowSymbol acceptNegative, MaskedEditShowSymbol displayMoney, string onFocusCssClass, string onFocusCssNegative, string onBlurCssNegative, string onInvalidCssClass)
        {
            RouteValueDictionary properties = new RouteValueDictionary();
            properties.Add("MaskType", MaskedEditType.Number);
            properties.Add("Mask", mask);
            properties.Add("AcceptNegative", acceptNegative);
            properties.Add("DisplayMoney", displayMoney);
            properties.AddIfNotDefault("OnFocusCssClass", onFocusCssClass);
            properties.AddIfNotDefault("OnFocusCssNegative", onFocusCssNegative);
            properties.AddIfNotDefault("OnBlurCssNegative", onBlurCssNegative);
            properties.AddIfNotDefault("OnInvalidCssClass", onInvalidCssClass);

            //return masked edit create statement
            return helper.MaskedEdit(elementId, properties);
        }

        #endregion

        #region [Dates]

        public static string MaskedEdit(this AjaxHelper helper, string elementId, string mask, MaskedEditUserDateFormat userDateFormat)
        {
            return helper.MaskedEdit(elementId, mask, userDateFormat, null, null, null, null);
        }

        public static string MaskedEdit(this AjaxHelper helper, string elementId, string mask, MaskedEditUserDateFormat userDateFormat, string onFocusCssClass)
        {
            return helper.MaskedEdit(elementId, mask, userDateFormat, onFocusCssClass, null, null, null);
        }

        public static string MaskedEdit(this AjaxHelper helper, string elementId, string mask, MaskedEditUserDateFormat userDateFormat, string onFocusCssClass, string onFocusCssNegative)
        {
            return helper.MaskedEdit(elementId, mask, userDateFormat, onFocusCssClass, onFocusCssNegative, null, null);
        }

        public static string MaskedEdit(this AjaxHelper helper, string elementId, string mask, MaskedEditUserDateFormat userDateFormat, string onFocusCssClass, string onFocusCssNegative, string onBlurCssNegative)
        {
            return helper.MaskedEdit(elementId, mask, userDateFormat, onFocusCssClass, onFocusCssNegative, onBlurCssNegative, null);
        }

        public static string MaskedEdit(this AjaxHelper helper, string elementId, string mask, MaskedEditUserDateFormat userDateFormat, string onFocusCssClass, string onFocusCssNegative, string onBlurCssNegative, string onInvalidCssClass)
        {
            RouteValueDictionary properties = new RouteValueDictionary();
            properties.Add("MaskType", MaskedEditType.Date);
            properties.Add("Mask", mask);
            properties.Add("UserDateFormat", userDateFormat);
            properties.AddIfNotDefault("OnFocusCssClass", onFocusCssClass);
            properties.AddIfNotDefault("OnFocusCssNegative", onFocusCssNegative);
            properties.AddIfNotDefault("OnBlurCssNegative", onBlurCssNegative);
            properties.AddIfNotDefault("OnInvalidCssClass", onInvalidCssClass);

            //return masked edit create statement
            return helper.MaskedEdit(elementId, properties);
        }

        #endregion

        #region [Times]

        public static string MaskedEdit(this AjaxHelper helper, string elementId, string mask, MaskedEditUserTimeFormat userTimeFormat, bool acceptAMPM)
        {
            return helper.MaskedEdit(elementId, mask, userTimeFormat, acceptAMPM, null, null, null, null);
        }

        public static string MaskedEdit(this AjaxHelper helper, string elementId, string mask, MaskedEditUserTimeFormat userTimeFormat, bool acceptAMPM, string onFocusCssClass)
        {
            return helper.MaskedEdit(elementId, mask, userTimeFormat, acceptAMPM, onFocusCssClass, null, null, null);
        }

        public static string MaskedEdit(this AjaxHelper helper, string elementId, string mask, MaskedEditUserTimeFormat userTimeFormat, bool acceptAMPM, string onFocusCssClass, string onFocusCssNegative)
        {
            return helper.MaskedEdit(elementId, mask, userTimeFormat, acceptAMPM, onFocusCssClass, onFocusCssNegative, null, null);
        }

        public static string MaskedEdit(this AjaxHelper helper, string elementId, string mask, MaskedEditUserTimeFormat userTimeFormat, bool acceptAMPM, string onFocusCssClass, string onFocusCssNegative, string onBlurCssNegative)
        {
            return helper.MaskedEdit(elementId, mask, userTimeFormat, acceptAMPM, onFocusCssClass, onFocusCssNegative, onBlurCssNegative, null);
        }

        public static string MaskedEdit(this AjaxHelper helper, string elementId, string mask, MaskedEditUserTimeFormat userTimeFormat, bool acceptAMPM, string onFocusCssClass, string onFocusCssNegative, string onBlurCssNegative, string onInvalidCssClass)
        {
            RouteValueDictionary properties = new RouteValueDictionary();
            properties.Add("MaskType", MaskedEditType.Time);
            properties.Add("Mask", mask);
            properties.Add("UserTimeFormat", userTimeFormat);
            properties.Add("AcceptAMPM", acceptAMPM);
            properties.AddIfNotDefault("OnFocusCssClass", onFocusCssClass);
            properties.AddIfNotDefault("OnFocusCssNegative", onFocusCssNegative);
            properties.AddIfNotDefault("OnBlurCssNegative", onBlurCssNegative);
            properties.AddIfNotDefault("OnInvalidCssClass", onInvalidCssClass);

            //return masked edit create statement
            return helper.MaskedEdit(elementId, properties);
        }

        #endregion

        public static string MaskedEdit(this AjaxHelper helper, string elementId, object properties)
        {
            RouteValueDictionary dictionary;

            if (properties is RouteValueDictionary)
            {
                dictionary = new RouteValueDictionary();
                dictionary.AddRange((properties as RouteValueDictionary));
            }
            else
                dictionary = new RouteValueDictionary(properties);
            
            //add state field
            dictionary.AddIfUnique("ClientStateFieldID", string.Format("{0}_{1}", elementId, "maskedEditState"));

            //add culture prop
            dictionary.AddIfUnique("CultureName", CultureInfo.CurrentCulture.Name);

            //create culture
            CultureInfo culture = new CultureInfo(dictionary["CultureName"].ToString());
            
            //add culture-specific properies
            dictionary.AddIfUnique("CultureAMPMPlaceholder", string.Format("{0};{1}", culture.DateTimeFormat.AMDesignator, culture.DateTimeFormat.PMDesignator));
            dictionary.AddIfUnique("CultureCurrencySymbolPlaceholder", culture.NumberFormat.CurrencySymbol);
            dictionary.AddIfUnique("CultureDateFormat", "MDY");
            dictionary.AddIfUnique("CultureDatePlaceholder", culture.DateTimeFormat.DateSeparator);
            dictionary.AddIfUnique("CultureDecimalPlaceholder", culture.NumberFormat.CurrencyDecimalSeparator);
            dictionary.AddIfUnique("CultureThousandsPlaceholder", culture.NumberFormat.CurrencyGroupSeparator);
            dictionary.AddIfUnique("CultureTimePlaceholder", culture.DateTimeFormat.TimeSeparator);

            StringBuilder sb = new StringBuilder();

            // Add Microsoft Ajax library   
            sb.Append(helper.MicrosoftAjaxInclude());

            // Add toolkit scripts   
            sb.Append(helper.ToolkitInclude(
                    "AjaxControlToolkit.ExtenderBase.BaseScripts.js",
                    "AjaxControlToolkit.Common.Common.js",
                    "AjaxControlToolkit.MaskedEdit.MaskedEditBehavior.js"
                ));

            sb.AppendFormat("<input type=\"hidden\" name=\"{0}_{1}\" id=\"{0}_{1}\" />", elementId, "maskedEditState");

            // Perform $create               
            sb.AppendLine(helper.Create("AjaxControlToolkit.MaskedEditBehavior", dictionary, elementId));

            return sb.ToString();
        }
    }

    #region [Enumerations]

    public enum MaskedEditType
    {
        None = 0,
        Date = 1,
        Number = 2,
        Time = 3,
        DateTime = 4
    }

    public enum MaskedEditUserDateFormat
    {
        None = 0,
        DayMonthYear = 1,
        DayYearMonth = 2,
        MonthDayYear = 3,
        MonthYearDay = 4,
        YearDayMonth = 5,
        YearMonthDay = 6
    }

    public enum MaskedEditUserTimeFormat
    {
        None = 0,
        TwentyFourHour = 1
    }

    public enum MaskedEditShowSymbol
    {
        None = 0,
        Left = 1,
        Right = 2
    }

    #endregion
}
